class Vec {

  constructor(firstArg, y) {
    if (y === undefined) {
      if (firstArg === undefined) {
        this.x = 0;
        this.y = 0;
      } else {
        const {x, y} = firstArg;
        this.x = x ?? 0;
        this.y = y ?? 0;
      }
    } else {
      this.x = firstArg;
      this.y = y;
    }
  }

  copy() {
    return new Vec(this);
  }
  
  plus({x=0, y=0}={}) {
    return new Vec({
      x: x + this.x,
      y: y + this.y,
    })
  }

  plus_({x=0, y=0}={}) {
    this.x += x;
    this.y += y;
  }

  diff({x, y}) {
    return new Vec({
      x: this.x - x,
      y: this.y - y
    })
  }
  
  diff_({x, y}) {
    this.x -= x;
    this.y -= y;
  }

  mult(t) {
    return new Vec({
      x: this.x * t,
      y: this.y * t,      
    })
  }

  mult_(t) {
    this.x *= t;
    this.y *= t;
  }
  
  perp() {
    return new Vec({
      x: -y, 
      y:  x
    });
  }

  spin(angle) {

    const {x, y} = this;
    const cosA = Math.cos(angle / 180 * Math.PI);
    const sinA = Math.sin(angle / 180 * Math.PI);

    return new Vec({
      x: x * cosA - y * sinA,
      y: x * sinA + y * cosA,
    })
  }
    
  /**
   * # (Vector) Cross product / 向量外积
   * 
   * Identical to finding determinant
   * 
   * |this.x   this.y|
   * |               |
   * |that.x   that.y|
   * 
   * @param {{x, y}} the other point
   */
  cross({x, y}) {
    return this.x * y - this.y * x;
  }

  dist({x, y}) {
    return Math.hypot(
      x - this.x,
      y - this.y
    );
  }
  
  mag() {
    return Math.hypot(this.x, this.y);
  }

  neg() {
    return new Vec({x: -this.x, y: -this.y});
  }

  norm() {
    return new Vec({
      x: this.x / this.mag(),
      y: this.y / this.mag(),
    })
  }

  lerp({x, y}, ratio) {
    return new Vec({
      x: this.x + (x - this.x) * ratio,
      y: this.y + (y - this.y) * ratio,
    })
  }
}
